﻿using EtherCAT.NET.Infrastructure;
using Jodell.Transport.Ethercat;
using Jodell.Transport.Ethercat.Esi;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;

namespace Jodell.Lua
{
    public class Lua_EcMaster : IDisposable
    {
        public bool IsOpened { get; private set; }
        private EcMaster master;

        public Lua_EcMaster()
        {
            master = new EcMaster();
            master.ConfigSlaveEsi += Master_ConfigSlaveEsi;
        }
        private static SubItemType FindSubItem(EtherCATInfoDescriptionsDevice device, UInt16 index, byte subIdx)
        {
            SubItemType _subItem = null;
            foreach (var pro in device.Profile)
            {
                var dic = pro.Item as DictionaryType;
                if (dic == null)
                    continue;
                string dataType = string.Empty;
                //查找数据对象
                foreach (var obj in dic.Objects)
                {
                    if (obj.Index.Value.ParseUInt16() == index)
                    {
                        dataType = obj.Type;
                        break;
                    }
                }
                //查找数据类型
                foreach (var dt in dic.DataTypes)
                {
                    if (dt.Name == dataType)
                    {
                        //找到对应子索引
                        foreach (SubItemType item in dt.Items)
                        {
                            if (item != null)
                            {
                                if (item.SubIdx.ParseByte() == subIdx)
                                {
                                    //找到对应数据类型
                                    _subItem = item;
                                    break;
                                }
                            }
                        }
                    }

                    if (_subItem != null)
                        break;
                }

                if (_subItem != null)
                    break;
            }
            return _subItem;
        }
        private void Master_ConfigSlaveEsi(object sender, EcSlaveConfigEventArgs e)
        {
            (var device, var group) = EsiUtilities.FindEsi(e.Slave.Manufacturer, e.Slave.ProductCode, e.Slave.Revision);
            if (device == null)
            {
                MessageBox.Show("","",MessageBoxButton.OK, MessageBoxImage.Error);
                e.IsCancel = true;
                return;
            }
            e.Slave.Image = (byte[])device.Item;
            //RxPdo
            foreach (var pdo in device.RxPdo)
            {
                int bitOffs = 0;
                foreach (var entry in pdo.Entry)
                {
                    //查找数据类型
                    var index = entry.Index.Value.ParseUInt16();
                    var subindex = entry.SubIndex.ParseByte();

                    var subItem = FindSubItem(device, index, subindex);
                    if (subItem == null)
                    {
                        throw new Exception(string.Format("查找数据类型失败！Index:{0:X4} SubIdx:{1:X2}", index, subindex));
                    }
                    e.Slave.RxPdo.Add(new Transport.Ethercat.Esi.SubItem()
                    {
                        BitOffs = bitOffs,
                        BitSize = subItem.BitSize,
                        Comment = subItem.Comment?.FirstOrDefault()?.Value,
                        Flags = subItem.Flags,
                        Index = index,
                        SubIdx = subindex,
                        Name = subItem.Name,
                        Type = subItem.Type,
                    });
                    bitOffs += subItem.BitSize;
                }
            }
            //TxPdo
            foreach (var pdo in device.TxPdo)
            {
                int bitOffs = 0;
                foreach (var entry in pdo.Entry)
                {
                    //查找数据类型
                    var index = entry.Index.Value.ParseUInt16();
                    var subindex = entry.SubIndex.ParseByte();

                    var subItem = FindSubItem(device, index, subindex);
                    if (subItem == null)
                    {
                        throw new Exception(string.Format("查找数据类型失败！Index:{0:X4} SubIdx:{1:X2}", index, subindex));
                    }
                    e.Slave.TxPdo.Add(new Transport.Ethercat.Esi.SubItem()
                    {
                        BitOffs = bitOffs,
                        BitSize = subItem.BitSize,
                        Comment = subItem.Comment?.FirstOrDefault()?.Value,
                        Flags = subItem.Flags,
                        Index = index,
                        SubIdx = subindex,
                        Name = subItem.Name,
                        Type = subItem.Type,
                    });
                    bitOffs += subItem.BitSize;
                }
            }
            //0x8000 SDO
            bool findSdo = false;
            foreach (var pro in device.Profile)
            {
                string dataType = string.Empty;
                var index = (UInt16)0x8000;
                //查找数据对象
                DictionaryType dic = pro.Item as DictionaryType;
                foreach (var obj in dic.Objects)
                {
                    if (obj.Index.Value.ParseUInt16() == index)
                    {
                        dataType = obj.Type;
                        break;
                    }
                }
                foreach (var dt in dic.DataTypes)
                {
                    if (dt.Name == dataType)
                    {
                        findSdo = true;
                        e.Slave.SdoBitSize = dt.BitSize;
                        //找到0x8000
                        int bitOffs = 0;
                        foreach (SubItemType subItem in dt.Items)
                        {
                            var subIdx = subItem.SubIdx.ParseByte();
                            if (subIdx == 0)
                            {
                                continue;
                            }
                            if (subIdx == 1)
                            {
                                bitOffs = subItem.BitOffs;
                                e.Slave.SdoBitSize -= bitOffs;
                            }
                            e.Slave.Sdo.Add(new SubItem()
                            {
                                BitOffs = subItem.BitOffs - bitOffs,
                                BitSize = subItem.BitSize,
                                Comment = subItem.Comment?.FirstOrDefault()?.Value,
                                Flags = subItem.Flags,
                                Index = index,
                                SubIdx = subIdx,
                                Name = subItem.Name,
                                Type = subItem.Type,
                            });
                        }
                    }
                    if (findSdo)
                        break;
                }
                if (findSdo)
                    break;
            }
        }

        public List<Lua_EcAdapter> FindAdapters()
        {
            var adps = master.FindAdapters();
            List<Lua_EcAdapter> _adps = new List<Lua_EcAdapter>();
            foreach (var adp in adps)
            {
                _adps.Add(new Lua_EcAdapter()
                {
                    Name = adp.Name,
                    Description = adp.Description,
                });
            }
            return _adps;
        }
        public List<Lua_EcSlave> Slaves { get; set; }=new List<Lua_EcSlave>();
        public void Start(string ifname)
        {
            try
            {
                master.Start(ifname);

                this.Slaves.Clear();
                foreach (var item in master.Slaves)
                {
                    var _slave = new Lua_EcSlave()
                    {
                        Image = item.Image,
                        OldCsa = item.OldCsa,
                        Csa = item.Csa,
                        Inputs = item.Inputs,
                        Manufacturer = item.Manufacturer,
                        Name = item.Name,
                        Outputs = item.Outputs,
                        ProductCode = item.ProductCode,
                        RequestOp = item.RequestOp,
                        Revision = item.Revision,
                        SdoBitSize = item.SdoBitSize,
                        Sdo = new List<Lua_SubItem>(),
                        RxPdo = new List<Lua_SubItem>(),
                        TxPdo = new List<Lua_SubItem>(),
                    };
                    foreach (var subItem in item.Sdo)
                    {
                        _slave.Sdo.Add(new Lua_SubItem()
                        {
                            BitOffs = subItem.BitOffs,
                            BitSize = subItem.BitSize,
                            Comment = subItem.Comment,
                            Flags = subItem.Flags,
                            Index = subItem.Index,
                            Name = subItem.Name,
                            SubIdx = subItem.SubIdx,
                            Type = subItem.Type,
                            Visibility = subItem.Visibility,
                        });
                    }
                    foreach (var subItem in item.TxPdo)
                    {
                        _slave.TxPdo.Add(new Lua_SubItem()
                        {
                            BitOffs = subItem.BitOffs,
                            BitSize = subItem.BitSize,
                            Comment = subItem.Comment,
                            Flags = subItem.Flags,
                            Index = subItem.Index,
                            Name = subItem.Name,
                            SubIdx = subItem.SubIdx,
                            Type = subItem.Type,
                            Visibility = subItem.Visibility,
                        });
                    }
                    foreach (var subItem in item.RxPdo)
                    {
                        _slave.RxPdo.Add(new Lua_SubItem()
                        {
                            BitOffs = subItem.BitOffs,
                            BitSize = subItem.BitSize,
                            Comment = subItem.Comment,
                            Flags = subItem.Flags,
                            Index = subItem.Index,
                            Name = subItem.Name,
                            SubIdx = subItem.SubIdx,
                            Type = subItem.Type,
                            Visibility = subItem.Visibility,
                        });
                    }
                    this.Slaves.Add(_slave);
                }
                IsOpened = true;
            }
            catch
            { }
        }
        public void Stop()
        {
            master?.Stop();
            IsOpened = false;
        }
        public int GetSlaveState(int slaveNo)
        {
            return master.GetSlaveState(slaveNo);
        }
        public int ReadSdo_UInt8(int slave, int index, int subindex, out byte data)
        {
            int ret;
            data = 0;
            try
            {
                data = master.ReadSdo<byte>((UInt16)slave, (UInt16)index, (byte)subindex, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int ReadSdo_UInt16(int slave, int index, int subindex, out UInt16 data)
        {
            int ret;
            data = 0;
            try
            {
                data = master.ReadSdo<UInt16>((UInt16)slave, (UInt16)index, (byte)subindex, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int ReadSdo_Int16(int slave, int index, int subindex, out Int16 data)
        {
            int ret;
            data = 0;
            try
            {
                data = master.ReadSdo<Int16>((UInt16)slave, (UInt16)index, (byte)subindex, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int ReadSdo_UInt32(int slave, int index, int subindex, out UInt32 data)
        {
            int ret;
            data = 0;
            try
            {
                data = master.ReadSdo<UInt32>((UInt16)slave, (UInt16)index, (byte)subindex, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int ReadSdo_Int32(int slave, int index, int subindex, out Int32 data)
        {
            int ret;
            data = 0;
            try
            {
                data = master.ReadSdo<Int32>((UInt16)slave, (UInt16)index, (byte)subindex, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int ReadSdo_Float(int slave, int index, int subindex, out Single data)
        {
            int ret;
            data = 0;
            try
            {
                data = master.ReadSdo<float>((UInt16)slave, (UInt16)index, (byte)subindex, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int ReadSdo_Double(int slave, int index, int subindex, out Double data)
        {
            int ret;
            data = 0;
            try
            {
                data = master.ReadSdo<Double>((UInt16)slave, (UInt16)index, (byte)subindex, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }

        public int WriteSdo_UInt8(int slave, int index, int subindex, byte data)
        {
            int ret;
            try
            {
                master.WriteSdo<byte>((UInt16)slave, (UInt16)index, (byte)subindex, data, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int WriteSdo_UInt16(int slave, int index, int subindex, UInt16 data)
        {
            int ret;
            try
            {
                master.WriteSdo<UInt16>((UInt16)slave, (UInt16)index, (byte)subindex, data, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int WriteSdo_Int16(int slave, int index, int subindex, Int16 data)
        {
            int ret;
            try
            {
                master.WriteSdo<Int16>((UInt16)slave, (UInt16)index, (byte)subindex, data, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int WriteSdo_UInt32(int slave, int index, int subindex, UInt32 data)
        {
            int ret;
            try
            {
                master.WriteSdo<UInt32>((UInt16)slave, (UInt16)index, (byte)subindex, data, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int WriteSdo_Int32(int slave, int index, int subindex, Int32 data)
        {
            int ret;
            try
            {
                master.WriteSdo<Int32>((UInt16)slave, (UInt16)index, (byte)subindex, data, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int WriteSdo_Float(int slave, int index, int subindex, Single data)
        {
            int ret;
            try
            {
                master.WriteSdo<float>((UInt16)slave, (UInt16)index, (byte)subindex, data, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }
        public int WriteSdo_Double(int slave, int index, int subindex, Double data)
        {
            int ret;
            try
            {
                master.WriteSdo<Double>((UInt16)slave, (UInt16)index, (byte)subindex, data, false);
                ret = 0;
            }
            catch
            {
                ret = -1;
            }
            return ret;
        }

        public int ReadPdo_UInt8(int slave, int index, int subindex, out byte data)
        {
            int ret = 0;
            data = 0;
            data = master.ReadPdo<Byte>((UInt16)slave, (UInt16)index, (byte)subindex);
            return ret;
        }
        public int ReadPdo_UInt16(int slave, int index, int subindex, out UInt16 data)
        {
            int ret = 0;
            data = 0;
            data = master.ReadPdo<UInt16>((UInt16)slave, (UInt16)index, (byte)subindex);
            return ret;
        }
        public int ReadPdo_Int16(int slave, int index, int subindex, out Int16 data)
        {
            int ret = 0;
            data = 0;
            data = master.ReadPdo<Int16>((UInt16)slave, (UInt16)index, (byte)subindex);
            return ret;
        }
        public int ReadPdo_UInt32(int slave, int index, int subindex, out UInt32 data)
        {
            int ret = 0;
            data = 0;
            data = master.ReadPdo<UInt32>((UInt16)slave, (UInt16)index, (byte)subindex);
            return ret;
        }
        public int ReadPdo_Int32(int slave, int index, int subindex, out Int32 data)
        {
            int ret = 0;
            data = 0;
            data = master.ReadPdo<Int32>((UInt16)slave, (UInt16)index, (byte)subindex);
            return ret;
        }
        public int ReadPdo_Float(int slave, int index, int subindex, out float data)
        {
            int ret = 0;
            data = 0;
            data = master.ReadPdo<float>((UInt16)slave, (UInt16)index, (byte)subindex);
            return ret;
        }
        public int ReadPdo_Double(int slave, int index, int subindex, out double data)
        {
            int ret = 0;
            data = 0;
            data = master.ReadPdo<double>((UInt16)slave, (UInt16)index, (byte)subindex);
            return ret;
        }

        public int WritePdo_UInt8(int slave, int index, int subindex, byte data)
        {
            int ret = 0;
            master.WritePdo<Byte>((UInt16)slave, (UInt16)index, (byte)subindex, data);
            return ret;
        }
        public int WritePdo_UInt16(int slave, int index, int subindex, UInt16 data)
        {
            int ret = 0;
            master.WritePdo<UInt16>((UInt16)slave, (UInt16)index, (byte)subindex, data);
            return ret;
        }
        public int WritePdo_Int16(int slave, int index, int subindex, Int16 data)
        {
            int ret = 0;
            master.WritePdo<Int16>((UInt16)slave, (UInt16)index, (byte)subindex, data);
            return ret;
        }
        public int WritePdo_UInt32(int slave, int index, int subindex, UInt32 data)
        {
            int ret = 0;
            master.WritePdo<UInt32>((UInt16)slave, (UInt16)index, (byte)subindex, data);
            return ret;
        }
        public int WritePdo_Int32(int slave, int index, int subindex, Int32 data)
        {
            int ret = 0;
            master.WritePdo<Int32>((UInt16)slave, (UInt16)index, (byte)subindex, data);
            return ret;
        }
        public int WritePdo_Float(int slave, int index, int subindex, float data)
        {
            int ret = 0;
            master.WritePdo<float>((UInt16)slave, (UInt16)index, (byte)subindex, data);
            return ret;
        }
        public int WritePdo_Double(int slave, int index, int subindex, double data)
        {
            int ret = 0;
            master.WritePdo<double>((UInt16)slave, (UInt16)index, (byte)subindex, data);
            return ret;
        }

        public void Dispose()
        {
            if (IsOpened)
            {
                master.Stop();
                master = null;
            }
        }
    }


    public class Lua_EcAdapter
    {
        public string Name { get; set; }
        public string Description { get; set; }
    }

    public class Lua_EcSlave
    {
        public string Name { get; set; }
        public IntPtr Inputs { get; set; }
        public IntPtr Outputs { get; set; }
        public UInt32 Manufacturer { get; set; }
        public UInt32 ProductCode { get; set; }
        public UInt32 Revision { get; set; }
        public UInt16 OldCsa { get; set; }
        public UInt16 Csa { get; set; }

        public byte[] Image { get; set; }
        public bool RequestOp { get; set; }
        public int SdoBitSize { get; set; }
        public List<Lua_SubItem> RxPdo { get; set; } = new List<Lua_SubItem>();
        public List<Lua_SubItem> TxPdo { get; set; } = new List<Lua_SubItem>();
        public List<Lua_SubItem> Sdo { get; set; } = new List<Lua_SubItem>();
    }

    public class Lua_SubItem
    {
        public UInt16 Index { get; set; }
        public byte SubIdx { get; set; }
        public string Name { get; set; }
        public string Type { get; set; }
        public string Comment { get; set; }
        public int BitSize { get; set; }
        public int BitOffs { get; set; }
        public SubItemTypeFlags Flags { get; set; }
        /// <summary>
        /// 是否可见
        /// </summary>
        public bool Visibility { get; set; } = true;
    }
}
